package com.sinevil.tcpTrans.tcp;

import com.sinevil.tcpTrans.entity.json.InstructionJson;
import com.sinevil.tcpTrans.entity.json.handlers.ClientSocketJsonHandle;
import com.sinevil.tcpTrans.entity.task.TaskQueue;
import com.sinevil.tcpTrans.utils.fileTrans.FileTranser;
import com.sinevil.tcpTrans.utils.log.ClientLogger;
import lombok.Getter;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.channels.*;
import java.security.NoSuchAlgorithmException;
import java.util.Iterator;

/**
 * @Classname TCPClientNode
 * @Description TODO
 * @Version 1.0.0
 * @Date 2025/1/5 19:27
 * @Created by sinevil
 */
public class TCPClientNode extends TCPTransNode{
    /**
     * 最大重试次数
     */
    private static final int MAX_RETRY_TIMES = 5;
    /**
     * 重试延迟毫秒
     */
    private static final long RETRY_DELAY_MS = 1000;
    /**
     * 指向服务端指令结点的套接字
     */
    protected SocketChannel serverSocketChannel;
    /**
     * 服务端主机地址
     */
    @Getter
    protected String HostName;
    protected int ServerPort;

    public TCPClientNode(int clientPort, String hostname, int serverPort) throws NoSuchAlgorithmException, IOException {
        super();
        this.HostName = hostname;
            this.Port = clientPort;
            this.ServerPort = serverPort;
            this.fileTranser = new FileTranser(null,this);
            this.socketJsonHandle = new ClientSocketJsonHandle(this, this.fileTranser);

            this.serverSocketChannel = SocketChannel.open();
            this.serverSocketChannel.bind(new InetSocketAddress(clientPort));
            this.serverSocketChannel.configureBlocking(false);
            this.selector = Selector.open();

            // 先注册 OP_CONNECT 事件
            this.serverSocketChannel.register(selector, SelectionKey.OP_CONNECT);
            this.serverSocketChannel.connect(new InetSocketAddress(hostname, serverPort));
            System.out.println("serverSocketChannel.isConnected: " + this.serverSocketChannel.isConnected());
        writeTaskQueueMap.put(serverSocketChannel,new TaskQueue());
    }
    @Override
    public void init() {

    }

    @Override
    public void start() {
        nodeThread = new Thread(() -> {
            try {
//                // 非阻塞模式下，需要调用 finishConnect() 来确认连接
//                boolean isConnected = this.serverSocketChannel.isConnected();


                this.serverSocketChannel.configureBlocking(false);
                while (true) {
                    // 阻塞等待就绪事件发生
                    selector.select();

                    // 获取所有就绪的SelectionKey集合
                    Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
                    while (iterator.hasNext()) {
                        SelectionKey key = iterator.next();
                        iterator.remove();
                        if (key.isConnectable()) {
                            SocketChannel socketChannel = (SocketChannel) key.channel();
                            if (socketChannel.isConnectionPending()) {
                                // 调用finishConnect并检查返回值
                                boolean finishConnectResult = socketChannel.finishConnect();
                                if (finishConnectResult){
                                    socketChannel.register(selector, SelectionKey.OP_READ);
                                    ClientLogger.LOGGER.info("已连接到服务器: {}",socketChannel.getRemoteAddress());
                                }
                                if (!finishConnectResult) {
                                    // 如果finishConnect失败，调用reconnect方法尝试重新连接
                                    reconnect();
                                }
                            }
                            // TODO log.info("已成功连接到服务器");
                        } else if (key.isReadable()) {
                            this.handleReadableEvent(key);
                        }
                        // 处理可写事件
                        else if (key.isWritable()) {
                            handleWritableEvent(key);
                        }

                    }
                }
            } catch (NoConnectionPendingException | ClosedChannelException e) {
                // TODO
                new RuntimeException("未连接上服务端或者因为意外情况导致服务端连接中断");
            } catch (IOException e) {
                // TODO log.error("客户端运行出现异常: {}", e.getMessage());
                // 可以根据具体情况进行更合理的错误恢复或资源释放等操作，比如尝试重新连接等
                throw new RuntimeException(e);
            } finally {
                releaseResources();
            }
        });
        nodeThread.start();
    }

    protected void releaseResources() {
        try {
            if (selector!= null) {
                selector.close();
            }
            if (this.serverSocketChannel != null) {
                serverSocketChannel.close();
            }
        } catch (IOException e) {
            // TODO log.error("释放客户端资源出现异常: {}", e.getMessage());
            ClientLogger.LOGGER.error("释放客户端资源出现异常: {}", e.getMessage());
        }
    }

    /**
     * 重新连接
     */
    protected void reconnect() {
        int retryCount = 0;
        boolean connected = false;
        while (retryCount < MAX_RETRY_TIMES &&!connected) {
            try {
                if (serverSocketChannel!= null) {
                    serverSocketChannel.close();
                }
                serverSocketChannel = SocketChannel.open();
                serverSocketChannel.bind(new InetSocketAddress(Port));
                if (selector!= null) {
                    selector.close();
                }
                selector = Selector.open();
                serverSocketChannel.connect(new InetSocketAddress(this.HostName, this.ServerPort));
                serverSocketChannel.configureBlocking(false);
                serverSocketChannel.register(selector, SelectionKey.OP_CONNECT);
                // 等待连接完成事件或者检查连接状态
                // 这里假设在其他地方有合适的逻辑来检查连接是否成功建立
                // 例如在start方法中的循环里通过isConnectable事件检查
                // 如果连接成功，设置connected为true
                connected = true;
            } catch (IOException e) {
                // 记录错误日志，例如可以使用日志框架记录详细的错误信息
                System.err.println("重新连接出错: " + e.getMessage());
                retryCount++;
                try {
                    Thread.sleep(RETRY_DELAY_MS);
                } catch (InterruptedException ex) {
                    Thread.currentThread().interrupt();
                    // 记录中断异常，例如使用日志框架记录
                    System.err.println("重试等待过程被中断: " + ex.getMessage());
                }
            }

        }
        if (!connected) {
            // 如果重试次数用完还没连接成功，进行更严重的错误处理
            System.err.println("经过多次重试后仍无法连接");
        }
    }

    /**
     * 向服务端发送信息
     * @param json
     */
    public void sendMessage(InstructionJson json) {
        sendMessage(this.serverSocketChannel,json);
    }

    /**
     * 客户端请求下载文件
     */
    public void requestFile(String path){
        this.fileTranser.downloadFile(serverSocketChannel,path);
//        this.sendMessage(serverSocketChannel,new InstructionJson(InstructionJson.ClIENT_REQUEST_FILE,"file("+path+")"));
    }

    /**
     * 客户端请求上传文件
     */
    public void uploadFile(String filePath) {
        this.fileTranser.addPendingUploadTask(serverSocketChannel, filePath);
    }
}